package MySQL::Explain;

use 5.008;
use strict;

our $VERSION = '0.01';

use Carp;
use Text::Table;

use base qw(Class::Accessor::Fast Exporter);
__PACKAGE__->mk_accessors(qw(dbh results total_rows));

our @EXPORT_OK = qw(explain_query);

sub explain_query {
    my $query = shift;
    my %args = @_;
    my $bind = delete $args{'bind'};
    return __PACKAGE__->new( %args )->explain( $query, bind => $bind );
}

sub new {
    my $proto = shift;
    my %args = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new( \%args );
    $self->init;
    return $self;
}

sub init {
    my $self = shift;

    die "Database handle (dbh) is mandatory"
        unless $self->dbh;

}

sub explain {
    my $self = shift;
    my $query = shift || croak "SQL query is mandatory argument";
    my %args = @_;
    
    $query = "EXPLAIN $query" unless $query =~ /^(\s+)?EXPLAIN\s+/i;

    my $sth = $self->dbh->prepare($query);
    $sth->execute( $args{'bind'}? @{$args{'bind'}} : () );
    
    my $total_rows = 1;
    my @result = ();
    while ( my $val = $sth->fetchrow_hashref ) {
        $total_rows *= $val->{rows};
        push @result , $val;
    }
    $self->total_rows($total_rows);
    $self->results( \@result );
    
    return $self;
}

sub print {
    my $self = shift;
    
    my $res = $self->results or return;
    
    
    my $table = Text::Table->new(keys %{$res->[0]});

    for (@$res) {
        $table->add(values %{$_});
    }
    
    print $table;
}

1;

=head1 NAME

MySQL::Explain

=head1 SYNOPSIS

    use MySQL::Explain qw(explain_query);

    my $dbh = DBI->connect("dbi:mysql:db" , "test" , "test" , {RaiseError => 1, AutoCommit => 1});

    # functional constructor
    my $explain = explain_query("SELECT * FROM test", dbh => $dbh);

    # OO way
    my $explain = MySQL::Explain->new( dbh => $dbh )->explain( "SELECT * FROM test" );

    my $results = $explain->results;
    $explain->print;

=head1 DESCRIPTION

=head1 FUNCTIONS

=head2 explain_query

Takes a query and hash with options. Constructs a new object of class
L<MySQL::Explain> and explain it at once.

Options are combination of L</new> and L</explain> methods' arguments,
for example:

    explain_query(
        "SELECT * FROM test WHERE id = ?",
        dbh => $dbh,
        bind => [1]
    );

=head1 METHODS

=head2 new

=head2 explain

=head2 print

=head1 AUTHOR

Mikhail Maluyk E<lt>mikhail.maluyk@gmail.comE<gt>

=head1 LICENSE

This package is distributed under the same terms as perl itself.

=cut

1;
